package org.dxy.domain.model.aggregate.impl;

import lombok.Builder;
import lombok.Getter;
import org.dxy.domain.command.process.*;
import org.dxy.domain.event.DomainEvent;
import org.dxy.domain.event.process.*;
import org.dxy.domain.model.aggregate.Process;
import org.dxy.domain.model.valobj.ProcessId;
import org.dxy.types.enums.ProcessStatusEnum;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @description: 工序聚合根实现类
 * @author: dxy 
 * @createTime: 2025/6/22 17:34 
 * @auther dxy
 */
@Getter
@Builder
public class ProcessAggRoot implements Process {
    private ProcessId processId;         // 工序唯一标识
    private String processCode;          // 工序编码
    private ProcessStatusEnum status;   // 工序当前状态（枚举类型）
    private Long routeId;                // 关联的工艺路线ID（工艺ID）
    private Long equipmentId;            // 关联的设备ID
    private Long workstationId;          // 关联的工作站ID


    // === 事件集合 ===
    private final List<DomainEvent> domainEvents = new ArrayList<>();

    // === 事件相关方法 ===
    @Override
    public List<DomainEvent> getDomainEvents() {
        return Collections.unmodifiableList(domainEvents);
    }

    @Override
    public void addDomainEvent(DomainEvent event) {
        domainEvents.add(event);
    }

    @Override
    public void clearDomainEvents() {
        domainEvents.clear();
    }

    @Override
    public boolean hasDomainEvents() {
        return !domainEvents.isEmpty();
    }

    @Override
    public boolean hasNoDomainEvents() {
        return domainEvents.isEmpty();
    }

    /**
     * 创建工序（静态工厂方法）
     * @param processId 工序ID
     * @param command 创建工序命令
     * @return 工序聚合根
     */
    public static Process create(Long processId, CreateProcessCommand command) {

        // 2. 创建聚合根实例
        ProcessAggRoot process = ProcessAggRoot.builder()
                .processId(ProcessId.of(processId))
                .processCode(command.processCode())
                .status(ProcessStatusEnum.INITIALIZED)
                .build();

        // 3. 创建并添加领域事件
        ProcessCreatedEvent event = new ProcessCreatedEvent(
                process.getProcessId().getValue(),
                process.getProcessCode(),
                process.getStatus().getKey()
        );

        // 4. 添加到事件列表
        process.addDomainEvent(event);

        return process;
    }



    @Override
    public ProcessStartedEvent start(StartProcessCommand command) {
        // 1. 业务规则校验
        if (this.status != ProcessStatusEnum.INITIALIZED) {
            throw new IllegalStateException("工序当前状态不允许启动");
        }

        // 2. 修改聚合状态
        this.status = ProcessStatusEnum.RUNNING;

        // 3. 创建并返回领域事件
        ProcessStartedEvent event = new ProcessStartedEvent(
                this.processId.getValue(),
                command.workOrderId(),
                command.equipmentId(),
                this.status.name()
        );

        // 4. 添加到事件列表
        this.addDomainEvent(event);

        return event;
    }

    @Override
    public ProcessPausedEvent pause(PauseProcessCommand command) {
        // 1. 业务规则校验
        if (this.status != ProcessStatusEnum.RUNNING) {
            throw new IllegalStateException("只有运行中的工序才能暂停");
        }

        // 2. 修改聚合状态
        this.status = ProcessStatusEnum.PAUSED;

        // 3. 创建并返回领域事件
        ProcessPausedEvent event = new ProcessPausedEvent(
                this.processId.getValue(),
                this.status.name()
        );

        // 4. 添加到事件列表
        this.addDomainEvent(event);

        return event;
    }

    @Override
    public ProcessResumedEvent resume(ResumeProcessCommand command) {
        // 1. 业务规则校验
        if (this.status != ProcessStatusEnum.PAUSED) {
            throw new IllegalStateException("只有暂停状态的工序才能恢复");
        }

        // 2. 修改聚合状态
        this.status = ProcessStatusEnum.RUNNING;

        // 3. 创建并返回领域事件
        ProcessResumedEvent event = new ProcessResumedEvent(
                this.processId.getValue(),
                this.status.name()
        );
        // 4. 添加到事件列表
        this.addDomainEvent(event);
        return event;
    }

    @Override
    public ProcessCompletedEvent complete(CompleteProcessCommand command) {
        // 1. 业务规则校验
        if (this.status != ProcessStatusEnum.RUNNING) {
            throw new IllegalStateException("只有运行中的工序才能标记为完成");
        }

        // 2. 修改聚合状态
        this.status = ProcessStatusEnum.COMPLETED;

        // 3. 创建并返回领域事件
        ProcessCompletedEvent event = new ProcessCompletedEvent(
                this.processId.getValue(),
                this.status.getKey()
        );

        // 4. 添加到事件列表
        this.addDomainEvent(event);

        return event;
    }

    @Override
    public ProcessFailedEvent fail(FailProcessCommand command) {
        // 1. 业务规则校验
        if (this.status == ProcessStatusEnum.COMPLETED || 
            this.status == ProcessStatusEnum.CANCELLED || 
            this.status == ProcessStatusEnum.ERROR) {
            throw new IllegalStateException("当前工序状态不允许标记为失败");
        }

        // 2. 修改聚合状态
        this.status = ProcessStatusEnum.ERROR;

        // 3. 创建并返回领域事件
        ProcessFailedEvent event = new ProcessFailedEvent(
                this.processId.getValue(),
                this.status.name()
        );

        // 4. 添加到事件列表
        this.addDomainEvent(event);

        return event;
    }

    @Override
    public ProcessStepChangedEvent changeStep(ChangeProcessStepCommand command) {
        // 1. 业务规则校验
        if (this.status != ProcessStatusEnum.RUNNING) {
            throw new IllegalStateException("只有运行中的工序才能变更步骤");
        }


        // 3. 创建并返回领域事件
        ProcessStepChangedEvent event = new ProcessStepChangedEvent(
                this.processId.getValue(),
                command.fromStepId(),
                command.toStepId()
        );

        // 4. 添加到事件列表
        this.addDomainEvent(event);

        return event;
    }

    @Override
    public ProcessQualityCheckedEvent checkQuality(CheckProcessQualityCommand command) {
        // 1. 业务规则校验
        if (this.status != ProcessStatusEnum.RUNNING) {
            throw new IllegalStateException("只有运行中的工序才能进行质检");
        }

        // 2. 修改聚合状态（如果质检不通过，状态变为ERROR）
        if ("FAIL".equals(command.result())) {
            this.status = ProcessStatusEnum.ERROR;
        }

        // 3. 创建并返回领域事件
        ProcessQualityCheckedEvent event = new ProcessQualityCheckedEvent(
                this.processId.getValue(),
                command.result(),
                this.status.name()
        );

        // 4. 添加到事件列表
        this.addDomainEvent(event);

        return event;
    }

    @Override
    public ProcessHoldEvent hold(HoldProcessCommand command) {
        // 1. 业务规则校验
        if (this.status == ProcessStatusEnum.COMPLETED || 
            this.status == ProcessStatusEnum.CANCELLED || 
            this.status == ProcessStatusEnum.ERROR) {
            throw new IllegalStateException("当前工序状态不允许挂起");
        }

        // 2. 修改聚合状态
        this.status = ProcessStatusEnum.HOLD;

        // 3. 创建并返回领域事件
        ProcessHoldEvent event = new ProcessHoldEvent(
                this.processId.getValue(),
                this.status.name()
        );

        // 4. 添加到事件列表
        this.addDomainEvent(event);

        return event;
    }

    @Override
    public ProcessReleasedEvent release(ReleaseProcessCommand command) {
        // 1. 业务规则校验
        if (this.status != ProcessStatusEnum.HOLD) {
            throw new IllegalStateException("只有挂起状态的工序才能解除挂起");
        }

        // 2. 修改聚合状态
        this.status = ProcessStatusEnum.RUNNING;

        // 3. 创建并返回领域事件
        ProcessReleasedEvent event = new ProcessReleasedEvent(
                this.processId.getValue(),
                this.status.name()
        );

        // 4. 添加到事件列表
        this.addDomainEvent(event);

        return event;
    }

    @Override
    public ProcessReworkedEvent rework(ReworkProcessCommand command) {
        // 1. 业务规则校验
        if (this.status == ProcessStatusEnum.COMPLETED || this.status == ProcessStatusEnum.CANCELLED) {
            throw new IllegalStateException("已完成或已取消的工序不能返工");
        }
        // 2. 修改聚合状态
        this.status = ProcessStatusEnum.INITIALIZED;
        // 3. 创建并返回领域事件
        ProcessReworkedEvent event = new ProcessReworkedEvent(
                this.processId.getValue(),
                this.status.getKey()
        );
        // 4. 添加到事件列表
        this.addDomainEvent(event);
        return event;
    }

    @Override
    public ProcessScrappedEvent scrap(ScrapProcessCommand command) {
        // 1. 业务规则校验
        if (this.status == ProcessStatusEnum.COMPLETED || this.status == ProcessStatusEnum.CANCELLED || this.status == ProcessStatusEnum.SCRAPPED) {
            throw new IllegalStateException("已完成、已取消或已报废的工序不能再次报废");
        }
        // 2. 修改聚合状态
        this.status = ProcessStatusEnum.SCRAPPED;
        // 3. 创建并返回领域事件
        ProcessScrappedEvent event = new ProcessScrappedEvent(
                this.processId.getValue(),
                this.status.name()
        );
        // 4. 添加到事件列表
        this.addDomainEvent(event);
        return event;
    }

    @Override
    public ProcessMeasurementRecordedEvent recordMeasurement(RecordProcessMeasurementCommand command) {
        // 1. 业务规则校验
        if (this.status == ProcessStatusEnum.COMPLETED || this.status == ProcessStatusEnum.CANCELLED || this.status == ProcessStatusEnum.STOPPED) {
            throw new IllegalStateException("已完成、已取消或已报废的工序不能记录测量参数");
        }
        // 2. 创建并返回领域事件
        ProcessMeasurementRecordedEvent event = new ProcessMeasurementRecordedEvent(
                this.processId.getValue()
        );
        // 3. 添加到事件列表
        this.addDomainEvent(event);
        return event;
    }

    @Override
    public void assign(AssignProcessCommand command) {
        // 校验业务规则
        if (this.status != ProcessStatusEnum.INITIALIZED) {
            throw new IllegalStateException("只有待分配状态的工序可以被分配");
        }

        // 修改内部状态
        this.workstationId = command.workstationId();
        this.equipmentId = command.equipmentId();
        this.status = ProcessStatusEnum.ASSIGNED;

        // 返回领域事件
        ProcessAssignedEvent event = new ProcessAssignedEvent(
                this.processId.getValue(),
                this.workstationId,
                this.equipmentId
        );

        this.addDomainEvent(event);
    }
} 